﻿using System;
using System.Collections.Generic;
using System.Text;

namespace TTEditor.Project
{
    public class Project
    {
        ProjectConfig config;
        public string Path
        {
            get;
            private set;
        }
        System.IO.FileSystemWatcher watcher;
        bool changed = false;
        public bool IsChange()
        {
            if (changed)
            {
                changed = false;
                return true;
            }
            else
            {
                return false;
            }
        }
        System.Collections.Concurrent.ConcurrentQueue<string> ModifyFile = new System.Collections.Concurrent.ConcurrentQueue<string>();
        System.Collections.Concurrent.ConcurrentQueue<string> DeleteFile = new System.Collections.Concurrent.ConcurrentQueue<string>();



        Path root = new Path();
        public Path GetFiles()
        {
            return root.DeepClone();
        }
        public string GetAssetPath(File f)
        {
            var path = System.IO.Path.Combine(this.Path, "assets");
            if (f.fullname.Contains(path))
            {
                return f.fullname.Substring(path.Length);
            }
            else
            {
                throw new Exception("not a asset file.");
            }
        }
        /// <summary>
        /// 檢查資源
        /// </summary>
        /// <param name="force">
        /// 强行檢查，無所謂有沒有文件發生改變
        /// </param>
        public void CheckResource(bool force = false)
        {
            //第一步 遍歷所有文件，如果有force，則把遍歷到的所有文件加入修改列表
            System.IO.DirectoryInfo dinfo = new System.IO.DirectoryInfo(System.IO.Path.Combine(this.Path, "assets"));
            CheckResourceInDirectory(dinfo, root, force);

            while (DeleteFile.TryDequeue(out string file))
            {
                ParseResourceDelete(file);
            }
            while (ModifyFile.TryDequeue(out string file))
            {
                ParseResourceModify(file);
            }
            if (force)
                changed = true;


        }
        void ParseResourceDelete(string file)
        {

        }
        void ParseResourceModify(string file)
        {
            //对每一个文件调用converter进行检查，如果需要导入则导入他
            //下一步，弄出UI，等等
            var fileonly = System.IO.Path.GetFileName(file);
            var converter = TTEditor.Resource.ConverterMgr.GetConverterByFileName(fileonly);
            if (converter == null)
            {
                return;
            }
            TTEditor.Resource.ConverterMgr.Convert(converter, file, file + ".meta", false);
        }
        void CheckResourceInDirectory(System.IO.DirectoryInfo dinfo, Path node, bool force)
        {
            node.Name = dinfo.Name;
            var files = dinfo.GetFiles();
            List<File> filenames = new List<File>();
            foreach (var f in files)
            {
                if (f.Extension.ToLower() == ".meta")
                    continue;
                else
                {
                    filenames.Add(new File() { Name = f.Name, fullname = f.FullName });
                    if (force)
                    {
                        ModifyFile.Enqueue(f.FullName);
                    }
                }
            }
            node.files = filenames.ToArray();
            var dirs = dinfo.GetDirectories();
            node.paths = new Path[dirs.Length];
            for (var i = 0; i < dirs.Length; i++)
            {
                node.paths[i] = new Path();
                CheckResourceInDirectory(dirs[i], node.paths[i], force);
            }
        }
        void StartWatcher()
        {
            watcher = new System.IO.FileSystemWatcher();
            watcher.Path = System.IO.Path.Combine(this.Path, "assets");
            watcher.IncludeSubdirectories = true;
            //這些事件都在另一個綫程，需要注意，
            //創建和修改可能引發編譯和導入操作
            //創建 刪除 改名會引起文件樹刷新
            //刪除可能會引起關聯資源丟失，要不要也得處理一下啊
            watcher.Changed += (s, e) =>
            {
                if (System.IO.Directory.Exists(e.FullPath))
                {//这个改变的可能是个目录

                }
                else
                {
                    ModifyFile.Enqueue(e.FullPath);
                    changed = true;
                }

            };
            watcher.Created += (s, e) =>
            {
                ModifyFile.Enqueue(e.FullPath);
                changed = true;
            };
            watcher.Deleted += (s, e) =>
            {
                DeleteFile.Enqueue(e.FullPath);
                changed = true;
            };
            watcher.Renamed += (s, e) =>
            {
                DeleteFile.Enqueue(e.OldFullPath);//刪除舊文件
                ModifyFile.Enqueue(e.FullPath);//添加新文件
                changed = true;
            };
            watcher.EnableRaisingEvents = true;
        }
        Project(string path)
        {
            this.Path = path;
            var projfile = System.IO.Path.Combine(path + "/" + configFileName);
            config = new ProjectConfig();
            config.Load(projfile);
        }
        const string projPublicTemplate = @"<Project Sdk=""Microsoft.NET.Sdk"">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <Compile Remove = ""**\**"" /><!--删除所有文件-->
    <Compile Include=""public\**"" /><!--仅包括指定路径中的文件-->
  </ItemGroup>
</Project>
";
        const string projAppTemplate = @"<Project Sdk=""Microsoft.NET.Sdk"">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <Compile Remove = ""**\**"" /><!--删除所有文件-->
    <Compile Include=""app\**"" /><!--仅包括指定路径中的文件-->
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include=""public.csproj"" />
  </ItemGroup>
</Project>
";
        const string projEditorTemplate = @"<Project Sdk=""Microsoft.NET.Sdk"">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <Compile Remove = ""**\**"" /><!--删除所有文件-->
    <Compile Include=""editor\**"" /><!--仅包括指定路径中的文件-->
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include=""public.csproj"" />
  </ItemGroup>
</Project>
";
        static string configFileName = "ttengine.config.json";
        public static Project Create(string path)
        {
            var projfile = System.IO.Path.Combine(path + "/" + configFileName);
            if (System.IO.File.Exists(projfile))
                throw new Exception("already have a fakeproj here.");
            ProjectConfig.Create(projfile);

            System.IO.File.WriteAllText(path + "/public.csproj", projPublicTemplate);
            System.IO.File.WriteAllText(path + "/app.csproj", projAppTemplate);
            System.IO.File.WriteAllText(path + "/editor.csproj", projEditorTemplate);

            var assetpath = System.IO.Path.Combine(path, "assets");
            if (System.IO.Directory.Exists(assetpath) == false)
                System.IO.Directory.CreateDirectory(assetpath);
            //Editor会重复初始化环境，Loader只初始化一次
            TTEngine.Environment.Init(TTEngine.Platform.Editor_Windows64, path);
            return Project.Open(path);
        }
        public static Project Open(string path)
        {
            var proj = new Project(path);
            proj.StartWatcher();
            proj.CheckResource(true);

            //Editor会重复初始化环境，Loader只初始化一次
            TTEngine.Environment.Reset();
            TTEngine.Environment.Init(TTEngine.Platform.Editor_Windows64, path);

            return proj;
        }
        public void Close()
        {
            this.watcher.Dispose();
            this.watcher = null;
        }

        public void Build(TTEngine.ILogger logger)
        {
            System.Diagnostics.ProcessStartInfo info = new System.Diagnostics.ProcessStartInfo();
            info.FileName = "dotnet";
            info.Arguments = "build editor.csproj";
            info.WorkingDirectory = this.Path;
            info.UseShellExecute = false;
            info.RedirectStandardOutput = true;
            info.RedirectStandardError = true;
            info.CreateNoWindow = true;
            System.Diagnostics.Process p = System.Diagnostics.Process.Start(info);
            p.OutputDataReceived += (s, e) =>
              {
                  if (e.Data == null)
                      return;
                  logger.Log(e.Data);
              };
            p.ErrorDataReceived += (s, e) =>
              {
                  if (e.Data == null)
                      return;
                  logger.Error(e.Data);
              };

            p.BeginOutputReadLine();
            p.BeginErrorReadLine();
        }
    }
    public class ProjectConfig
    {
        string version = "0.0.1(test)";
        public static ProjectConfig Create(string file)
        {
            Newtonsoft.Json.Linq.JObject ojb = new Newtonsoft.Json.Linq.JObject();
            ProjectConfig config = new ProjectConfig();

            ojb["FakeUnityVersion"] = config.version;

            var str = ojb.ToString(Newtonsoft.Json.Formatting.Indented);
            System.IO.File.Delete(file);
            System.IO.File.WriteAllText(file, str);
            return config;
        }
        public void Load(string file)
        {
            var txt = System.IO.File.ReadAllText(file);
            Newtonsoft.Json.Linq.JObject ojb = Newtonsoft.Json.Linq.JObject.Parse(txt);

            this.version = (string)ojb["FakeUnityVersion"];
        }



    }
}
